import java.lang.reflect.*;

/**
 * This class represents a Node of the SpaceTree.
 * 
 * @author (Dilyan GEORGIEV)
 * @author (Thomas KOBER)
 * @author (Matthew MCDONALD)
 * 
 * @version (1.0)
 */
public class SpaceNode<E> implements Cloneable
{
    ////////////////////////////////////////////////////////////////////////////////
    // Field declarations
    ////////////////////////////////////////////////////////////////////////////////
    
    // The Nodes data
    private E data;
    // The Nodes left child
    private SpaceNode<E> left;
    // The Nodes right child
    private SpaceNode<E> right;
    // The Nodes parent
    private SpaceNode<E> parent;
    // Defines if the node is already been visited
    private boolean alreadyVisited;

    ////////////////////////////////////////////////////////////////////////////////
    // Constructor(s)
    ////////////////////////////////////////////////////////////////////////////////
    
    /**
     * Constructor for objects of class SpaceNode.
     * @param parent the Nodes parent.
     * @param data the Nodes data.
     */
    public SpaceNode(SpaceNode<E> parent, E data)
    {
        super();
        
        this.data = data;
        this.left = null;
        this.right = null;
        this.parent = parent;
        this.alreadyVisited = false;
    }

    ////////////////////////////////////////////////////////////////////////////////
    // Public Implementation
    ////////////////////////////////////////////////////////////////////////////////
    
    /**
     * Returns the Nodes data.
     * @return the Nodes data.
     */
    public E getData()
    {
        return this.data;
    }
    
    /**
     * Returns the Nodes left child.
     * @return the Nodes left child.
     */
    public SpaceNode<E> getLeft()
    {
        return this.left;
    }
    
    /**
     * Sets the Nodes left child to the given Node.
     * @param the Nodes new left child.
     */
    public void setLeft(SpaceNode<E> left)
    {
        this.left = left;
    }
    
    /**
     * Returns the Nodes right child.
     * @return the Nodes right child.
     */
    public SpaceNode<E> getRight()
    {
        return this.right;
    }
    
    /**
     * Sets the Nodes right child to the given Node.
     * @param the Nodes new right child.
     */
    public void setRight(SpaceNode<E> right)
    {
        this.right = right;
    }
    
    /**
     * Returns whether or not the current node has a right child.
     * @return true if the current node has a right child, false otherwise.
     */
    public boolean hasRight()
    {
        return this.right != null;
    }
    
    
    /**
     *  Marks the node as visited.
     *  @param a boolean if it has been visited.
     */
    public void setAlreadyVisited(boolean visit) {
        this.alreadyVisited = true;
    }
    
    
    
    /**
     * Returns whether or not the current node has been visited.
     * @return true if the current node has been visited, false otherwise.
     */
    public boolean isAlreadyVisited() {
        return this.alreadyVisited;
    }
    
    
    /**
     * Returns whether or not the current node has a left child.
     * @return true if the current node has a left child, false otherwise.
     */
    public boolean hasLeft()
    {
        return this.left != null;
    }
    /**
     * Returns the Nodes parent.
     * @return the Nodes parent.
     */
    public SpaceNode<E> getParent()
    {
        return this.parent;
    }
    
    /**
     * Sets the Nodes parent to the given Node.
     * @param parent the Nodes new parent.
     */
    public void setParent(SpaceNode<E> parent)
    {
        this.parent = parent;
    }

    // Cloneable implementation
    public SpaceNode clone()
    {
        SpaceNode theCopy = null;
        
        try {
            
            Class[] params = null;
            
            // For some reasons cannot call clone on data (greenfoot runtime fail?), therefore using Reflection
            Method cloneMethod = data.getClass().getDeclaredMethod("clone", params);
            theCopy = (SpaceNode)super.clone();
            theCopy.data = cloneMethod.invoke(data);
            
        } catch (CloneNotSupportedException ex) {
            ex.printStackTrace();
        } catch (NoSuchMethodException ex) {
            ex.printStackTrace();
        } catch (IllegalAccessException ex) {
            ex.printStackTrace();
        } catch (InvocationTargetException ex) {
            ex.printStackTrace();
        }
        
        return theCopy;
    }
    
    
    
    public boolean equals(Object o)
    {
        boolean equals = false;
        
        if (o != null) {
            if (o == this) {
                equals = true;
            } else {
                if (o instanceof SpaceNode) {
                    SpaceNode<E> other = (SpaceNode<E>)o;
                    
                    if (this.getData().equals(other.getData())) {
                        equals = true;
                    }
                }
            }
        }
        
        return equals;
    }
    
    public int hashCode()
    {
        int result = 17;
        
        result = 31 * result + this.getData().hashCode();
        
        return result;
    }
}
